/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package net.sf.l2j.gameserver.model.entity.eventengine;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;

import net.sf.l2j.gameserver.ThreadPoolManager;
import net.sf.l2j.gameserver.model.L2World;
import net.sf.l2j.gameserver.model.Location;
import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
import net.sf.l2j.gameserver.network.serverpackets.CreatureSay;

/**
 * @author Anarchy
 *
 */
public abstract class AbstractEvent
{
	public Map<String, ScheduledFuture<?>> _tasks = new HashMap<String, ScheduledFuture<?>>();
	public List<EventPlayer> _players = new ArrayList<EventPlayer>();
	public List<L2PcInstance> _playersObjs = new ArrayList<L2PcInstance>();
	public EventState _state = EventState.INACTIVE;
	
	public void addEventToList()
	{
		if (EventManager.getInstance().addEventToList(this))
		{
			scheduleEventStart(getEventTimes());
		}
		else
		{
			System.out.println("Could not load event: "+getName());
		}
	}
	
	public void scheduleEventStart(String eventTimes)
	{
		Map<Integer, Integer> times = new HashMap<Integer, Integer>();
		String[] eventTimes_splitted = eventTimes.split(";");
		for (String s : eventTimes_splitted)
		{
			String[] ss = s.split(":");
			times.put(Integer.valueOf(ss[0]), Integer.valueOf(ss[1]));
		}
		
		Calendar cld = Calendar.getInstance();
		for (int i : times.keySet())
		{
			cld.set(Calendar.HOUR_OF_DAY, i);
			cld.set(Calendar.MINUTE, times.get(i));
			cld.set(Calendar.SECOND, 0);
			
			long cldTime = cld.getTimeInMillis(),
				startTime = cldTime-System.currentTimeMillis();
			
			// TODO: REMOVE THIS AFTER TESTS.
			System.out.println("EventManager: Next event in "+startTime+" milliseconds.");
			
			_tasks.put(getName(), ThreadPoolManager.getInstance().scheduleGeneral(new EventStart(), startTime));
		}
	}
	
	public void runEvent()
	{
		int waitTime = 0;
		EventManager.getInstance().setEventActive(true);
		EventManager.getInstance().setActiveEvent(this);
		_state = EventState.REGISTERING;
		announce(getName()+" event started. You have "+getTimeFormat(getWaitTimes()[waitTime])+" to register.");
		wait(getWaitTimes()[waitTime]);
		waitTime++;
		
		if (_players.size() < getMinPlayers())
		{
			announce("The event was cancelled because there were not enough participants.");
			_players.clear();
			EventManager.getInstance().setEventActive(false);
			return;
		}
		
		announce("Event registrations are now over. Players will be teleported in "+getTimeFormat(getWaitTimes()[waitTime])+".");
		_state = EventState.TELEPORTING;
		wait(getWaitTimes()[waitTime]);
		waitTime++;
		
		for (EventPlayer p : _players)
		{
			p.saveXYZ();
			p.getPlayer().teleToLocation(getEventLocation(), true);
		}
		onTeleported();
		announce("Players where teleported. Event will begin in "+getTimeFormat(getWaitTimes()[waitTime])+".");
		wait(getWaitTimes()[waitTime]);
		waitTime++;
		
		_state = EventState.RUNNING;
		onStart();
		announce("Event has started. It will end in "+getTimeFormat(getWaitTimes()[waitTime])+".");
		wait(getWaitTimes()[waitTime]);
		
		_state = EventState.INACTIVE;
		EventManager.getInstance().setEventActive(false);
		announce("Event has finished.");
		onEnd();
		_players.clear();
		_playersObjs.clear();
	}
	
	public void announce(String msg)
	{
		CreatureSay cs = new CreatureSay(0, 15, "Event Manager", msg);
		for (L2PcInstance p : L2World.getInstance().getAllPlayers().values())
		{
			p.sendPacket(cs);
		}
	}
	
	public void wait(int seconds)
	{
		try
		{
			Thread.sleep(seconds * 1000);
		}
		catch (InterruptedException e)
		{ }
	}
	
	public String getTimeFormat(int seconds)
	{
		if (seconds >= 60)
		{
			return seconds/60 > 1 ? seconds/60+" minutes" : "1 minute";
		}
		
		return seconds > 1 ? seconds+" seconds" : "1 second";
	}
	
	public EventPlayer getEventPlayerObject(L2PcInstance p)
	{
		for (EventPlayer ep : _players)
		{
			if (ep.getPlayer() == p)
			{
				return ep;
			}
		}
		
		return null;
	}
	
	protected class EventReviveTask implements Runnable
	{
		private L2PcInstance _player = null;
		private int _x = 0,
			_y = 0,
			_z = 0;
		
		public EventReviveTask(L2PcInstance p, int x, int y, int z)
		{
			_player = p;
			_x = x;
			_y = y;
			_z = z;
		}
		
		@Override
		public void run()
		{
			_player.doRevive();
			_player.teleToLocation(_x, _y, _z);
			_player.setCurrentHpMp(_player.getMaxHp(), _player.getMaxMp());
			_player.setCurrentCp(_player.getMaxCp());
		}
	}
	
	private class EventStart implements Runnable
	{
		@Override
		public void run()
		{
			runEvent();
			scheduleEventStart(getEventTimes());
		}
	}
	
	// Overridden methods.
	public abstract void onTeleported();
	public abstract void onStart();
	public abstract void onEnd();
	public abstract void onKill(L2PcInstance killer, L2PcInstance p);
	public abstract int getMinPlayers();
	public abstract Location getEventLocation();
	public abstract boolean isTeamEvent();
	public abstract String getName();
	public abstract String getEventTimes(); // Format: HH:MM;HH:MM
	public abstract int[] getWaitTimes();
}
